#ifndef TB_MATH_HPP_
#define TB_MATH_HPP_

#include "math_export.hpp"

#ifdef INFINITY
#undef INFINITY
#endif

#ifdef FLT_EPSILON
#undef FLT_EPSILON
#endif

#ifdef Sign
#undef Sign
#endif
#ifdef Square
#undef Square
#endif
#ifdef Cube
#undef Cube
#endif

TB_NAMESPACE_BEGIN 

namespace math {

TB_MATH_API void            Init( void );

TB_MATH_API double				  RSqrt( double x );			// reciprocal square root, returns huge number when x == 0.0

TB_MATH_API double				  InvSqrt( double x );			// inverse square root with 32 bits precision, returns huge number when x == 0.0
TB_MATH_API double				  InvSqrt16( double x );		// inverse square root with 16 bits precision, returns huge number when x == 0.0
TB_MATH_API double			    InvSqrt64( double x );		// inverse square root with 64 bits precision, returns huge number when x == 0.0

TB_MATH_API double				  Sqrt( double x );			// square root with 32 bits precision
TB_MATH_API double				  Sqrt16( double x );			// square root with 16 bits precision
TB_MATH_API double			    Sqrt64( double x );			// square root with 64 bits precision

TB_MATH_API double				  Sin( double a );				// sine with 32 bits precision
TB_MATH_API double				  Sin16( double a );			// sine with 16 bits precision, maximum absolute error is 2.3082e-09
TB_MATH_API double			    Sin64( double a );			// sine with 64 bits precision

TB_MATH_API double				  Cos( double a );				// cosine with 32 bits precision
TB_MATH_API double				  Cos16( double a );			// cosine with 16 bits precision, maximum absolute error is 2.3082e-09
TB_MATH_API double			    Cos64( double a );			// cosine with 64 bits precision

TB_MATH_API void				    SinCos( double a, double &s, double &c );		// sine and cosine with 32 bits precision
TB_MATH_API void				    SinCos16( double a, double &s, double &c );	// sine and cosine with 16 bits precision
TB_MATH_API void				    SinCos64( double a, double &s, double &c );	// sine and cosine with 64 bits precision

TB_MATH_API double				  Tan( double a );				// tangent with 32 bits precision
TB_MATH_API double				  Tan16( double a );			// tangent with 16 bits precision, maximum absolute error is 1.8897e-08
TB_MATH_API double			    Tan64( double a );			// tangent with 64 bits precision

TB_MATH_API double				  ASin( double a );			// arc sine with 32 bits precision, input is clamped to [-1, 1] to avoid a silent NaN
TB_MATH_API double				  ASin16( double a );			// arc sine with 16 bits precision, maximum absolute error is 6.7626e-05
TB_MATH_API double			    ASin64( double a );			// arc sine with 64 bits precision

TB_MATH_API double				  ACos( double a );			// arc cosine with 32 bits precision, input is clamped to [-1, 1] to avoid a silent NaN
TB_MATH_API double				  ACos16( double a );			// arc cosine with 16 bits precision, maximum absolute error is 6.7626e-05
TB_MATH_API double			    ACos64( double a );			// arc cosine with 64 bits precision

TB_MATH_API double				  ATan( double a );			// arc tangent with 32 bits precision
TB_MATH_API double				  ATan16( double a );			// arc tangent with 16 bits precision, maximum absolute error is 1.3593e-08
TB_MATH_API double			    ATan64( double a );			// arc tangent with 64 bits precision

TB_MATH_API double				  ATan( double y, double x );	// arc tangent with 32 bits precision
TB_MATH_API double				  ATan16( double y, double x );	// arc tangent with 16 bits precision, maximum absolute error is 1.3593e-08
TB_MATH_API double			    ATan64( double y, double x );	// arc tangent with 64 bits precision

TB_MATH_API double				  Pow( double x, double y );	// x raised to the power y with 32 bits precision
TB_MATH_API double				  Pow16( double x, double y );	// x raised to the power y with 16 bits precision
TB_MATH_API double			    Pow64( double x, double y );	// x raised to the power y with 64 bits precision

TB_MATH_API double				  Exp( double f );				// e raised to the power f with 32 bits precision
TB_MATH_API double				  Exp16( double f );			// e raised to the power f with 16 bits precision
TB_MATH_API double			    Exp64( double f );			// e raised to the power f with 64 bits precision

TB_MATH_API double				  Log( double f );				// natural logarithm with 32 bits precision
TB_MATH_API double				  Log16( double f );			// natural logarithm with 16 bits precision
TB_MATH_API double			    Log64( double f );			// natural logarithm with 64 bits precision

TB_MATH_API int					    IPow( int x, int y );		// integral x raised to the power y
TB_MATH_API int					    ILog2( double f );			// integral base-2 logarithm of the doubleing point value
TB_MATH_API int					    ILog2( int i );				// integral base-2 logarithm of the integer value

TB_MATH_API int					    BitsForFloat( double f );	// minumum number of bits required to represent ceil( f )
TB_MATH_API int					    BitsForInteger( int i );	// minumum number of bits required to represent i
TB_MATH_API int					    MaskForFloatSign( double f );// returns 0x00000000 if x >= 0.0f and returns 0xFFFFFFFF if x <= -0.0f
TB_MATH_API int					    MaskForIntegerSign( int i );// returns 0x00000000 if x >= 0 and returns 0xFFFFFFFF if x < 0
TB_MATH_API int					    FloorPowerOfTwo( int x );	// round x down to the nearest power of 2
TB_MATH_API int					    CeilPowerOfTwo( int x );	// round x up to the nearest power of 2
TB_MATH_API bool				    IsPowerOfTwo( int x );		// returns true if x is a power of 2
TB_MATH_API int					    BitCount( int x );			// returns the number of 1 bits in x
TB_MATH_API int					    BitReverse( int x );		// returns the bit reverse of x

TB_MATH_API int					    Abs( int x );				// returns the absolute value of the integer value (for reference only)
TB_MATH_API double				  Fabs( double f );			// returns the absolute value of the doubleing point value
TB_MATH_API double				  Floor( double f );			// returns the largest integer that is less than or equal to the given value
TB_MATH_API double				  Ceil( double f );			// returns the smallest integer that is greater than or equal to the given value
TB_MATH_API double				  Rint( double f );			// returns the nearest integer
TB_MATH_API int					    Ftoi( double f );			// double to int conversion
TB_MATH_API int					    FtoiFast( double f );		// fast double to int conversion but uses current FPU round mode (default round nearest)
TB_MATH_API unsigned long		Ftol( double f );			// double to long conversion
TB_MATH_API unsigned long		FtolFast( double );			// fast double to long conversion but uses current FPU round mode (default round nearest)

TB_MATH_API signed char		  ClampChar( int i );
TB_MATH_API signed short	  ClampShort( int i );
TB_MATH_API int					    ClampInt( int min, int max, int value );
TB_MATH_API double				  ClampFloat( double min, double max, double value );

TB_MATH_API double				  AngleNormalize360( double angle );
TB_MATH_API double				  AngleNormalize180( double angle );
TB_MATH_API double				  AngleDelta( double angle1, double angle2 );

TB_MATH_API int					    DoubleToBits( double f, int exponentBits, int mantissaBits );
TB_MATH_API double				  BitsToDouble( int i, int exponentBits, int mantissaBits );

TB_MATH_API int					    DoubleHash( const double *array, const int numDoubles );

static const double			      PI = 3.14159265358979323846;							  // pi
static const double			      TWO_PI = 2.0 * PI;						              // pi * 2
static const double			      HALF_PI = 0.5 * PI;					              // pi / 2
static const double			      ONEFOURTH_PI = 0.25f * PI;			            // pi / 4
static const double			      E = 2.71828182845904523536f;							  // e
static const double			      SQRT_TWO = 1.41421356237309504880f;					// sqrt( 2 )
static const double			      SQRT_THREE = 1.73205080756887729352f;				// sqrt( 3 )
static const double			      SQRT_1OVER2 = 0.70710678118654752440f;			// sqrt( 1 / 2 )
static const double			      SQRT_1OVER3 = 0.57735026918962576450f;			// sqrt( 1 / 3 )
static const double			      M_DEG2RAD = PI / 180.0;				            // degrees to radians multiplier
static const double			      M_RAD2DEG = 180.0 / PI;				            // radians to degrees multiplier
static const double			      M_SEC2MS = 1000.0;					                // seconds to milliseconds multiplier
static const double			      M_MS2SEC = 0.001;					                // milliseconds to seconds multiplier
static const double			      INFINITY = 1e30;					                  // huge number which should be larger than any valid number used
static const double			      FLT_EPSILON = 1.192092896e-07;			        // smallest positive number such that 1.0+FLT_EPSILON != 1.0

} // namespace math

TB_NAMESPACE_END

#define DEG2RAD(a)				          ( (a) * TB_NAMESPACE::math::M_DEG2RAD )
#define RAD2DEG(a)				          ( (a) * TB_NAMESPACE::math::M_RAD2DEG )

#define SEC2MS(t)				            ( TB_NAMESPACE::math::FtoiFast( (t) * TB_NAMESPACE::math::M_SEC2MS ) )
#define MS2SEC(t)				            ( (t) * TB_NAMESPACE::math::M_MS2SEC )

#define	ANGLE2SHORT(x)			        ( TB_NAMESPACE::math::FtoiFast( (x) * 65536.0f / 360.0f ) & 65535 )
#define	SHORT2ANGLE(x)			        ( (x) * ( 360.0f / 65536.0f ) )

#define	ANGLE2BYTE(x)			          ( TB_NAMESPACE::math::FtoiFast( (x) * 256.0f / 360.0f ) & 255 )
#define	BYTE2ANGLE(x)			          ( (x) * ( 360.0f / 256.0f ) )

#define FLOATSIGNBITSET(d)		      ((*(const unsigned long *)&(d)) >> 31)
#define FLOATSIGNBITNOTSET(d)	      ((~(*(const unsigned long *)&(d))) >> 31)
#define FLOATNOTZERO(d)			        ((*(const unsigned long *)&(d)) & ~(1<<31) )
#define INTSIGNBITSET(i)		        (((const unsigned long)(i)) >> 31)
#define INTSIGNBITNOTSET(i)		      ((~((const unsigned long)(i))) >> 31)

#define	FLOAT_IS_NAN(x)			        (((*(const unsigned long *)&x) & 0x7f800000) == 0x7f800000)
#define FLOAT_IS_INF(x)			        (((*(const unsigned long *)&x) & 0x7fffffff) == 0x7f800000)
#define FLOAT_IS_IND(x)			        ((*(const unsigned long *)&x) == 0xffc00000)
#define	FLOAT_IS_DENORMAL(x)	      (((*(const unsigned long *)&x) & 0x7f800000) == 0x00000000 && \
  ((*(const unsigned long *)&x) & 0x007fffff) != 0x00000000 )

#define IEEE_FLT_MANTISSA_BITS	    23
#define IEEE_FLT_EXPONENT_BITS	    8
#define IEEE_FLT_EXPONENT_BIAS	    127
#define IEEE_FLT_SIGN_BIT		        31

#define IEEE_DBL_MANTISSA_BITS	    52
#define IEEE_DBL_EXPONENT_BITS	    11
#define IEEE_DBL_EXPONENT_BIAS	    1023
#define IEEE_DBL_SIGN_BIT		        63

#define IEEE_DBLE_MANTISSA_BITS	    63
#define IEEE_DBLE_EXPONENT_BITS	    15
#define IEEE_DBLE_EXPONENT_BIAS	    0
#define IEEE_DBLE_SIGN_BIT		      79

template<class T> TB_INLINE int	  MaxIndex(T x, T y) { return  (x > y) ? 0 : 1; }
template<class T> TB_INLINE int	  MinIndex(T x, T y) { return (x < y) ? 0 : 1; }

template<class T> TB_INLINE T	    Max3(T x, T y, T z) { return (x > y) ? ((x > z) ? x : z) : ((y > z) ? y : z); }
template<class T> TB_INLINE T	    Min3(T x, T y, T z) { return (x < y) ? ((x < z) ? x : z) : ((y < z) ? y : z); }
template<class T> TB_INLINE int	  Max3Index(T x, T y, T z) { return (x > y) ? ((x > z) ? 0 : 2) : ((y > z) ? 1 : 2); }
template<class T> TB_INLINE int	  Min3Index(T x, T y, T z) { return (x < y) ? ((x < z) ? 0 : 2) : ((y < z) ? 1 : 2); }

template<class T> TB_INLINE T	    Sign(T f) { return (f > 0) ? 1 : ((f < 0) ? -1 : 0); }
template<class T> TB_INLINE T	    Square(T x) { return x * x; }
template<class T> TB_INLINE T	    Cube(T x) { return x * x * x; }


#endif //TB_MATH_HPP_
